package dq.study.concurrent.monitor;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author dongqun
 * @date 2020/7/7 10:20
 */
public class Depot {
    private int capacity;
    private volatile AtomicInteger size = new AtomicInteger(0);
    private Lock lock;
    private Condition consumerCond;
    private Condition produceCond;

    Depot(int capacity) {
        this.capacity = capacity;
        this.lock = new ReentrantLock();
        this.consumerCond = lock.newCondition();
        this.produceCond = lock.newCondition();
    }

    void produce(int val) {
        lock.lock();
        try {
            int left = val;
            while (left > 0) {
                while (size.get()  >= capacity) {
                    produceCond.await();
                }
                int produce = (val + size.get()) > capacity ? (capacity - size.get()) : val;
                size.getAndAdd(produce);
                left -= produce;
                System.out.println(Thread.currentThread().getName() + ", 生产了=" + produce + ", 仓库剩余=" + size);
                consumerCond.signalAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    void consumer(int val) {
        lock.lock();
        try {
            int left = val;
            while (left > 0) {
                while (size.get() <= 0) {
                    consumerCond.await();
                }
                int consumer = (size.get() <= val) ? size.get() : val;
                size.getAndAdd(-consumer);
                left -= consumer;
                System.out.println(Thread.currentThread().getName() + ", 消费了=" + consumer + ", 仓库剩余=" + size);
                produceCond.signalAll();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    static class Produce {
        private Depot depot;

        Produce(Depot depot) {
            this.depot = depot;
        }

        void produceThing(final int amount) throws InterruptedException {
            new Thread(() -> depot.produce(amount)).start();
            Thread.sleep(1000);
        }
    }

    static class Consumer {
        private Depot depot;

        Consumer(Depot depot) {
            this.depot = depot;
        }

        void consumerThing(final int amount) throws InterruptedException {
            new Thread(() -> depot.consumer(amount)).start();
            Thread.sleep(1000);
        }
    }
    /**
     * 仓库例子
     * 结果：
     * <p>
     *      Thread-0, 生产了=5, 仓库剩余=5
     *      Thread-1, 消费了=5, 仓库剩余=0
     *      Thread-2, 生产了=2, 仓库剩余=2
     *      Thread-3, 消费了=2, 仓库剩余=0
     *      Thread-4, 生产了=3, 仓库剩余=3
     *      Thread-3, 消费了=3, 仓库剩余=0
     * </p>
     *
     * threa3执行了两次 ，第一次循环的时候因为size小于val，因此left>0 进入第二遍循环，
     * 此时size<0 condition await 线程阻塞，当thread4执行时consumerCond.signalAll();会唤醒所有等待线程，因此thread3继续执行
     *
     */
    public static void main(String[] args) throws InterruptedException {
        // 仓库
        Depot depot = new Depot(100);
        // 消费者
        Consumer consumer = new Consumer(depot);
        // 生产者
        Produce produce = new Produce(depot);
        produce.produceThing(5);
        consumer.consumerThing(5);
        produce.produceThing(2);
        consumer.consumerThing(5);
        produce.produceThing(3);
    }
}
